<template>
  <div>
    <div
      class="canvas"
      :style="{ width: leftPosition + 'px', height: topPosition + 'px' }"
      ref="canvasRef"
      v-if="showMap"
    >
      <div class="topContent">
        <div class="topIcon" />
      </div>
      <svg width="100%" height="100%" v-if="lines.length">
        <line
          v-for="(line, index) in lines"
          :key="index"
          :x1="line.x1"
          :y1="line.y1"
          :x2="line.x2"
          :y2="line.y2"
          :stroke="line.isHackLine ? '#202020' : 'red'"
          :class="handleFlowAnimation(line)"
          :stroke-dasharray="handleFlowLineType(line)"
        />
        <defs>
          <linearGradient
            id="blue-gradient"
            x1="0%"
            y1="0%"
            x2="100%"
            y2="100%"
          >
            <stop offset="0%" style="stop-color: #add8e6; stop-opacity: 1" />
            <!-- 淡蓝色 -->
            <stop offset="100%" style="stop-color: #b0e0e6; stop-opacity: 1" />
            <!-- 更深的淡蓝色 -->
          </linearGradient>
          <filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
            <feGaussianBlur in="SourceAlpha" stdDeviation="3" />
            <!-- 阴影模糊 -->
            <feOffset dx="2" dy="2" result="offsetblur" />
            <feFlood flood-color="rgba(0, 0, 0, 0.5)" />
            <!-- 阴影颜色 -->
            <feComposite in2="offsetblur" operator="in" />
            <feMerge>
              <feMergeNode />
              <!-- 合并阴影 -->
              <feMergeNode in="SourceGraphic" />
              <!-- 合并原圆 -->
            </feMerge>
          </filter>
        </defs>
        <circle
          v-for="(point, index) in points"
          :key="'circle-' + index + stationInfo.id"
          :cx="point?.startX"
          :cy="point?.startY"
          r="4"
          fill="url(#blue-gradient)"
          filter="url(#shadow)"
          id="circle-animation"
        >
          <!-- 水平移动动画 -->
          <animate
            attributeName="cx"
            :from="point?.startX"
            :to="point?.endX"
            dur="0.7s"
            fill="freeze"
            :begin="
              (point.electronStatus === ELECTRON_STATUS_ENUM.charge ? 0 : 0.7) +
              point.index +
              currentTimeline +
              's'
            "
          />
          <!-- 垂直移动动画 -->
          <animate
            attributeName="cy"
            :from="point?.startY"
            :to="point?.endY"
            dur="0.7s"
            fill="freeze"
            :begin="
              (point.electronStatus === ELECTRON_STATUS_ENUM.charge ? 0.7 : 0) +
              point.index +
              currentTimeline +
              's'
            "
          />
        </circle>
        <circle
          v-for="(fuZaipoint, index) in fuZaiPoints"
          :key="'fuZaiPoints-' + index + stationInfo.id"
          :cx="fuZaipoint?.[0]?.startX"
          :cy="fuZaipoint?.[0]?.startY"
          r="4"
          fill="url(#blue-gradient)"
          filter="url(#shadow)"
          id="circle-animation"
          zIndx="10"
        >
          <animate
            attributeName="cy"
            :from="fuZaipoint?.[0]?.startY"
            :to="fuZaipoint?.[0]?.endY"
            dur="0.7s"
            :begin="0.7 + currentTimeline + 's'"
            fill="freeze"
          />
          <animate
            attributeName="cx"
            :from="fuZaipoint?.[1]?.startX"
            :to="fuZaipoint?.[1]?.endX"
            dur="1s"
            :begin="1.5 + currentTimeline + 's'"
            fill="freeze"
          />
          <animate
            attributeName="cy"
            :from="fuZaipoint?.[2]?.startY"
            :to="fuZaipoint?.[2]?.endY"
            dur="0.5s"
            fill="freeze"
            :begin="2.5 + currentTimeline + 's'"
          />
        </circle>
        <circle
          v-for="(bingWangGuiPoint, index) in bingWangGuiPoints"
          :key="'bingWangGuiPoints-' + index + stationInfo.id"
          :cx="bingWangGuiPoint?.[0]?.startX"
          :cy="bingWangGuiPoint?.[0]?.startY"
          r="4"
          fill="url(#blue-gradient)"
          filter="url(#shadow)"
          id="circle-animation"
          zIndx="10"
        >
          <animate
            attributeName="cy"
            :from="bingWangGuiPoint?.[0]?.startY"
            :to="bingWangGuiPoint?.[0]?.endY"
            dur="0.7s"
            :begin="0.7 + currentTimeline + 's'"
            fill="freeze"
          />
          <animate
            attributeName="cx"
            :from="bingWangGuiPoint?.[1]?.startX"
            :to="bingWangGuiPoint?.[1]?.endX"
            dur="1s"
            :begin="1.5 + currentTimeline + 's'"
            fill="freeze"
          />
          <animate
            attributeName="cy"
            :from="bingWangGuiPoint?.[2]?.startY"
            :to="bingWangGuiPoint?.[2]?.endY"
            dur="0.5s"
            fill="freeze"
            :begin="2.5 + currentTimeline + 's'"
          />
        </circle>
      </svg>
      <div
        v-for="element in elements"
        :key="element.id"
        :class="[
          'canvas-element',
          element.type === EQUIP_TYPE.LINE && element.y === 40
            ? 'customLineBar'
            : '',
          element.type === EQUIP_TYPE.DIAN_CHI_CU ? 'cursor' : '',
        ]"
        :style="{
          top: element.y + 'px',
          left:
            (element.type === EQUIP_TYPE.LINE && element.y === 40
              ? '120'
              : element.x) + 'px',
        }"
        :draggable="false"
      >
        <div
          v-if="element.type === EQUIP_TYPE.DIAN_CHI_CU"
          class="percent"
          :style="{ height: 30 * getSocVal(element) + 'px' }"
        />
        <div v-if="element.type === EQUIP_TYPE.LINE" class="line">
          <!-- <span class="lineName">{{ element.name }}</span> -->
          <div class="lineName">
            <span> {{ element.lineName }} </span>
          </div>
          <span
            class="customLine"
            :style="{
              width: element.width + 'px',
              height: element.height + 'px',
            }"
          />
        </div>
        <!-- 储充项目 充电桩拓扑 -->
        <ChongDianZahung
          v-if="element.type === EQUIP_TYPE.CHONG_DIAN_ZHUANG"
          :element="element"
          @handleEquip="handleEquip"
        />
        <!-- 集装箱项目 拓扑 -->
        <JiZhuangXiang
          v-if="element.type === EQUIP_TYPE.JI_ZHUANG_XIANG"
          :element="element"
          @handleEquip="handleEquip"
        />
        <div
          v-if="
            element.type !== EQUIP_TYPE.LINE &&
            element.type !== EQUIP_TYPE.CHONG_DIAN_ZHUANG &&
            element.type !== EQUIP_TYPE.JI_ZHUANG_XIANG
          "
          :class="[
            'elementEquip',
            element.isChildEquip || element.type === EQUIP_TYPE.GUAN_KOU_BIAO
              ? 'isChildEquip'
              : '',
          ]"
          :style="{
            backgroundImage: `url(${element.url})`,
            backgroundRepeat: 'no-repeat',
            backgroundSize: 'contain',
            width: '50px',
            height: '50px',
            position: 'relative',
          }"
          @click="handleEquip(element)"
        >
          <!-- 资产编码 -->
          <div class="assertCode" v-if="element?.assertCodeData?.assetCode">
            <!-- <div>{{ assetName(element) }}</div> -->
            <div v-if="element?.systemId">
              <div>
                资产编码：{{ element?.assertCodeData?.assetCode || "--" }}
              </div>
              <div>
                资产编码释义：{{
                  element?.assertCodeData?.assetCodeExplain ||
                  element?.assertCodeData?.meterAssetCodeExplain ||
                  "--"
                }}
              </div>
            </div>
            <div v-else>
              <div>
                资产编码：{{ element?.assertCodeData?.meterAssetCode || "--" }}
              </div>
              <div>
                资产编码释义：{{
                  element?.assertCodeData?.meterAssetCodeExplain || "--"
                }}
              </div>
            </div>
          </div>
        </div>
        <div
          v-if="
            element.type !== EQUIP_TYPE.LINE &&
            element.type !== EQUIP_TYPE.CHONG_DIAN_ZHUANG &&
            element.type !== EQUIP_TYPE.JI_ZHUANG_XIANG
          "
          class="canvasElementName"
        >
          {{ element.name }}
        </div>
        <div
          :class="[
            'detailContent',
            element.type === EQUIP_TYPE.JI_ZHUANG_XIANG
              ? 'jiZhuangXiangDetailContent'
              : '',
          ]"
          v-if="element.infoData?.length"
        >
          <div
            :class="`detailItem, ${infoItem.name}`"
            v-for="(infoItem, infoIndex) in element.infoData.filter(
              (item) => !filterField.includes(item.name) // 这个是状态
            )"
            :key="infoIndex"
          >
            <div
              v-if="
                infoItem.name !== 'linkElectronStatus' &&
                infoItem.name !== 'blockElectronStatus'
              "
            >
              {{ infoItem.name }}:
              <span v-param="infoItem.pdId"
                >{{ infoItem.value }} {{ infoItem.unit }}</span
              >
            </div>
            <div
              v-if="
                infoItem.name === 'linkElectronStatus' ||
                infoItem.name === 'blockElectronStatus'
              "
            >
              状态：{{ ELECTRON_STATUS_LIST[infoItem.value] }}
            </div>
          </div>
        </div>
        <div
          :class="[
            'linkStatus',
            element.type === EQUIP_TYPE.CHONG_DIAN_ZHUANG
              ? 'cdzLinkStatus'
              : '',
          ]"
          v-if="element.type !== EQUIP_TYPE.JI_ZHUANG_XIANG"
        >
          <div v-if="element.lineStatus === 0" class="offline">离线</div>
          <div
            v-if="element?.warnData && Object.keys(element?.warnData)?.length"
            class="warnning"
          >
            告警
            <div :class="['linkDetailContent']">
              <!-- 告警信息，hover显示 -->
              <div class="warnMessage">
                <div v-if="element?.warnData?.warnLevel">
                  告警等级：{{ `${element?.warnData?.warnLevel}级` || "--" }}
                </div>
                <div>
                  告警信息：{{ element?.warnData?.warnMessage || "--" }}
                </div>
                <div>告警触发：{{ element?.warnData?.comment || "--" }}</div>
                <div>
                  规则备注：{{ element?.warnData?.maintenanceAdvice || "--" }}
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
    <DianChiCu
      v-else
      :elementData="elementData"
      :pcsData="pcsData"
      :batteryInfo="batteryInfo"
      :assetCodeData="assetCodeData"
    />
  </div>
</template>
<script setup>
import {
  EQUIP_TYPE,
  ELECTRON_STATUS_ENUM,
  ELECTRON_STATUS_LIST,
} from "@/constants/stationConfig";
import { commonConfigStore } from "@/stores/commonConfig";
const { changeShowBackBtn } = commonConfigStore();
import { debounce, cloneDeep, uniqBy } from "lodash-es";
import DianChiCu from "./DianChiCu.vue";
import ChongDianZahung from "./ChongDianZhuang.vue";
import JiZhuangXiang from "./JiZhuangXiang.vue";
const props = defineProps({
  topologyMap: {
    type: Object,
  },
  stationInfo: {
    type: Object,
  },
});

const filterField = ["electronStatus"]; // 过滤掉不展示在拓扑图上的字段
const $api = inject("$api");
const elements = ref([]);
const lines = ref([]);
const lineName = ref("");
const canvasRef = ref(null);
const showMap = ref(true);
const assetName = (element) => {
  if (element?.type === EQUIP_TYPE.BIAN_YA_QI) {
    return "变压器";
  }
  if (element?.type === EQUIP_TYPE.BING_WANG_GUI) {
    return "并网柜";
  }
};
const gatewayAssetName = (element) => {
  if (element?.type === EQUIP_TYPE.BIAN_YA_QI) {
    return "关口表";
  }
  if (element?.type === EQUIP_TYPE.BING_WANG_GUI) {
    return "并网表";
  }
};
// 更新连接线,根据父子元素连接线
const connectLines = async () => {
  lines.value = []; // 重置连接线
  const bianYaQI = elements.value.filter(
    (item) => item.type === EQUIP_TYPE.BIAN_YA_QI
  );
  const guanKouBiao = elements.value.filter(
    (item) => item.type === EQUIP_TYPE.GUAN_KOU_BIAO
  );
  // const gateWayP = bianYaQI.children.find((item) => item.type === EQUIP_TYPE.GUAN_KOU_BIAO);
  const drawParentChildLines = (element) => {
    // 只有在直接父子关系中绘制连接线
    for (const child of element.children) {
      if (element.type !== EQUIP_TYPE.LINE && child.type !== EQUIP_TYPE.LINE) {
        drawLine(element, child, guanKouBiao); // 绘制父亲到子级的线
        drawParentChildLines(child); // 递归到子级，确保子级继续绘制
      }
    }
  };
  let lineArr = elements.value.filter((elem) => elem.type === EQUIP_TYPE.LINE);
  const cloneLineArr = cloneDeep(lineArr);
  // 如果是充电桩项目 isChargeProject 为 1，需要遍历查找elements.value，如果A的parentId为B的systemId，那么B的linkElectronStatus的值等于A的linkElectronStatus的值
  if (props.stationInfo?.isChargeProject === 1) {
    const elementsMap = {};
    elements.value.forEach((element) => {
      if (element.systemId) {
        elementsMap[element.systemId] = element;
      }
    });
    // 更新linkElectronStatus
    elements.value.forEach((elementA) => {
      if (elementA.parentId && elementsMap[elementA.parentId]) {
        const elementB = elementsMap[elementA.parentId];
        if (elementA.linkElectronStatus) {
          elementB.linkElectronStatus = elementA.linkElectronStatus;
        }
      }
    });
  }

  for (const element of elements.value) {
    // 如果此元素是顶级元素（没有父元素），则绘制连接线到顶部
    if (
      !element.parentId &&
      element.parentId !== 0 &&
      element.type !== EQUIP_TYPE.LINE
    ) {
      // 找出在器部件上方位置的线

      lineArr = cloneLineArr.filter(
        (line) =>
          (element.x >= line.x - 25 && element.x <= line.x + line.width - 25) ||
          (line.y === 40 && line.x === undefined)
      );

      if (lineArr.length) {
        const lineClosestToChild = lineArr.reduce((closest, current) => {
          const currentDiff = Math.abs(current.y - element.y + 25);
          const closestDiff = Math.abs(closest.y - element.y + 25);
          return currentDiff < closestDiff ? current : closest;
        });

        drawLine(
          { x: element.x, y: lineClosestToChild.y - 50 },
          element,
          guanKouBiao
        ); // 连接线到顶部电线
      }
    }

    // 为集装箱的电池簇绘制连线
    elements.value.forEach((element) => {
      if (element.type === EQUIP_TYPE.JI_ZHUANG_XIANG && element.children) {
        const batteryClusters = element.children.filter(
          (child) => child.type === EQUIP_TYPE.BATTERY_CLUSTER
        );
        batteryClusters.forEach((cluster) => {
          // 从电池簇到集装箱的连线
          const clusterX = cluster.x + 25; // 电池簇中心X
          const clusterY = cluster.y; // 电池簇顶部Y
          const targetX = element.x + 25; // 集装箱中心X
          const targetY = element.y + 25; // 集装箱中心Y
          // 绘制连线：先竖直，再水平
          lines.value.push({
            x1: clusterX,
            y1: clusterY,
            x2: clusterX,
            y2: targetY,
          });
          lines.value.push({
            x1: clusterX,
            y1: targetY,
            x2: targetX,
            y2: targetY,
          });
        });
      }
    });

    // 仅绘制此元素的子级，防止跨代连接线
    drawParentChildLines(element);
  }
  const uniqueData = (data) => {
    const result = [];
    const map = new Map();
    data.forEach((item) => {
      const key = `${item.x1},${item.x2},${item.y1}`;
      if (map.has(key)) {
        // 如果存在于 map 中，比较 y2 值
        const existing = map.get(key);
        if (item.y2 > existing.y2) {
          map.set(key, item); // 替换为 y2 更大的数据，为了处理例如变压器下方有关口表，导致出现两条线的问题
        }
      } else {
        // 如果不存在，则添加到 map 中
        map.set(key, item);
      }
    });
    // 从 map 中提取最终的数据
    map.forEach((item) => result.push(item));
    return result;
  };
  lines.value = uniqueData(lines.value); // 去重
  lines.value = lines.value.filter((item) => item.parentName || item.childName);
};
// 绘制连接线
const drawLine = (parent, child, guanKouBiao) => {
  const startX = parent.x + 25; // 父元素中心X
  const startY = parent.y + 25; // 父元素中心Y
  let endX; // 子元素中心X
  let endY; // 子元素中心Y
  if (child.type === EQUIP_TYPE.LINE) {
    endX = parent.x + 25;
    endY = child.y;
  } else if (child.type === EQUIP_TYPE.CHONG_DIAN_ZHUANG) {
    endX = child.x + 150;
    endY = child.y;
  } else {
    endX = child.x + 25;
    endY = child.y + 25;
  }
  let gateWayP = null;
  if (
    parent.type === EQUIP_TYPE.BIAN_YA_QI ||
    child.type === EQUIP_TYPE.BIAN_YA_QI
  ) {
    const bianYaQiSystemId = parent.systemId || child.systemId;
    if (bianYaQiSystemId) {
      gateWayP = guanKouBiao
        .find((item) => item.parentId === bianYaQiSystemId)
        ?.infoData?.find((item) => item.name === "P")?.value;
    }
  }
  const addLine = (x1, y1, x2, y2) => {
    lines.value.push({
      x1,
      y1,
      x2,
      y2,
      parent,
      parentName: parent.name,
      parentId: parent.systemId,
      parentType: parent.type,
      child,
      childName: child.name,
      childId: child.systemId,
      childType: child.type,
      isChildEquip: child.isChildEquip,
      linkElectronStatus: parent.linkElectronStatus || child.linkElectronStatus,
      blockElectronStatus:
        child.infoData?.find((item) => item.name === "blockElectronStatus")
          ?.value || child.blockElectronStatus,
      gateWayP,
    });
  };
  if (startX === endX) {
    // 竖直线
    const yAdjustment = startY < endY ? 25 : -25;
    addLine(startX, startY + yAdjustment, endX, endY);
  } else if (startY === endY) {
    // 水平线
    const xAdjustment = startX < endX ? 25 : -25;
    addLine(startX + xAdjustment, startY, endX, endY);
  } else {
    // 计算相对方向并先绘制横线，再绘制竖线
    addLine(startX, startY, endX, startY); // 添加横线
    addLine(endX, startY, endX, endY); // 添加竖线
  }
};
const points = ref([]);
const convert = (items) => {
  const prefixList = items.filter((item) => item.type !== EQUIP_TYPE.LINE);
  const map = new Map();
  const roots = [];
  prefixList.forEach((item) => {
    map.set(item.systemId, { ...item, children: [] });
  });
  Array.from(map.values()).forEach((node) => {
    const parentId = node.parentId;
    if (parentId !== null && parentId !== undefined) {
      const parent = map.get(parentId);
      parent?.children.push(node);
    } else {
      roots.push(node);
    }
  });
  // 分配index
  const assignIndex = (items, currentIndex) => {
    for (const item of items) {
      item.index = currentIndex - 1; // 设置当前节点的 index
      if (item.children) {
        assignIndex(item.children, currentIndex + 1);
      }
    }
  };
  assignIndex(roots, 0);
  return roots;
};
const leftPosition = ref(20000);
const topPosition = ref(2000);
const findObjectsByType = (nodes, type) => {
  const result = [];
  const traverse = (node) => {
    if (Array.isArray(node)) {
      node.forEach((child) => traverse(child));
    } else if (typeof node === "object" && node !== null) {
      if (node.type === type) {
        result.push(node);
      }
      if (node.children) {
        traverse(node.children);
      }
    }
  };
  traverse(nodes);
  return result;
};
const originFuZaiPoints = ref([]);
const handleFuZaiAnimate = () => {
  // 处理 变压器 => 负载 动效
  let bianYaQiData = cloneDeep(
    findObjectsByType(elements.value, EQUIP_TYPE.BIAN_YA_QI)
  );
  const fuZaiData = cloneDeep(
    findObjectsByType(elements.value, EQUIP_TYPE.FU_ZAI)
  );
  let lineArr = elements.value.filter((elem) => elem.type === EQUIP_TYPE.LINE);
  const cloneLineArr = cloneDeep(lineArr);
  if (fuZaiData.length) {
    bianYaQiData = uniqBy(bianYaQiData, "id");
    for (const element of bianYaQiData) {
      if (
        !element.parentId &&
        element.parentId !== 0 &&
        element.type !== EQUIP_TYPE.LINE
      ) {
        // 找出在变压器下方位置的线
        lineArr = cloneLineArr
          .map((line) => ({ ...line }))
          .filter(
            (line) =>
              (element.x >= line.x - 25 &&
                element.x <= line.x + line.width - 25) ||
              (line.y === 40 && line.x === undefined)
          );

        if (lineArr.length) {
          const lineClosestToChild = lineArr.reduce((closest, current) => {
            const currentDiff = Math.abs(current.y - element.y + 25);
            const closestDiff = Math.abs(closest.y - element.y + 25);
            return currentDiff > closestDiff ? current : closest;
          });
          // 找出在线下方位置的负载
          const fuZaiClosestToLine = fuZaiData.filter(
            (fuZaielement) =>
              fuZaielement.x >= lineClosestToChild.x - 25 &&
              fuZaielement.x <=
                lineClosestToChild.x + lineClosestToChild.width - 25
          );
          if (fuZaiClosestToLine.length) {
            lineClosestToChild.children = [fuZaiClosestToLine[0]];
          }
          if (lineClosestToChild) {
            lineClosestToChild.index = 1;
            lineClosestToChild.x = element.x;
            lineClosestToChild.y = lineClosestToChild.y - 25;
            element.children = [lineClosestToChild];
            element.index = 0;
          }
        }
      }
    }
    bianYaQiData = bianYaQiData.filter((d) => d);
    bianYaQiData.forEach((item, index) => {
      fuZaiPoints.value[index] = [];
      fuZaiGenerateLinesAndPoints(item, index);
    });
    originFuZaiPoints.value = cloneDeep(fuZaiPoints.value);
  }
};
const originBingWangGuiPoints = ref([]);
const handleBingWangGuiAnimate = () => {
  // 处理 并网柜 => 负载 动效
  let bingWangGuiData = cloneDeep(
    findObjectsByType(elements.value, EQUIP_TYPE.BING_WANG_GUI)
  );
  const fuZaiData = cloneDeep(
    findObjectsByType(elements.value, EQUIP_TYPE.FU_ZAI)
  );
  let lineArr = elements.value.filter((elem) => elem.type === EQUIP_TYPE.LINE);

  // const cloneLineArr = cloneDeep(lineArr);
  const cloneLineArr = lineArr.map((line) => ({ ...line }));

  if (fuZaiData.length) {
    for (const element of bingWangGuiData) {
      // 找出在并网柜上方位置的线
      const upLineArr = cloneLineArr
        .map((line) => ({ ...line }))
        .filter((line) => {
          return (
            element.x >= line.x - 25 && element.x <= line.x + line.width - 25
          );
        });
      if (upLineArr.length) {
        const lineClosestToChild = upLineArr.reduce((closest, current) => {
          const currentDiff = Math.abs(current.y - element.y + 25);
          const closestDiff = Math.abs(closest.y - element.y + 25);
          return currentDiff < closestDiff ? current : closest;
        });

        // 找出在线下方位置的负载
        const fuZaiClosestToLine = fuZaiData.filter(
          (fuZaielement) =>
            fuZaielement.x >= lineClosestToChild.x &&
            fuZaielement.x <=
              lineClosestToChild.x + lineClosestToChild.width - 25
        );
        if (fuZaiClosestToLine.length) {
          lineClosestToChild.children = [fuZaiClosestToLine[0]];
        }
        if (lineClosestToChild) {
          lineClosestToChild.index = 1;
          lineClosestToChild.x = cloneDeep(element.x);
          lineClosestToChild.y = lineClosestToChild.y - 25;
          element.children = [lineClosestToChild];
          element.index = 0;
        }
      }
    }
    bingWangGuiData = uniqBy(bingWangGuiData, "id").filter((d) => d);
    bingWangGuiData.forEach((item, index) => {
      bingWangGuiPoints.value[index] = [];
      bingWangGuiGenerateLinesAndPoints(item, index);
    });
    originBingWangGuiPoints.value = cloneDeep(bingWangGuiPoints.value);
  }
};
const handleElectronAnimate = () => {
  let elData = cloneDeep(elements.value);
  elData = elData.map((item) => {
    const { children, ...rest } = item;
    return {
      ...rest,
    };
  });
  elData = convert(elData.filter((item) => !item.isChildEquip));
  elData = findObjectsByType(elData, EQUIP_TYPE.BING_WANG_GUI);
  const topElData = []; // 这个是动效的顶层数据，即顶层器部件=>电池堆
  for (const item of elData) {
    const childData = item?.children;
    if (!childData) {
      return;
    }
    for (const childItem of childData) {
      const newItem = {
        ...item,
        children: [childItem],
      };
      topElData.push(newItem);
    }
  }
  // 绘制顶层器部件=>电池堆
  for (const item of topElData) {
    if (item.children[0].linkElectronStatus === ELECTRON_STATUS_ENUM.charge) {
      generateLinesAndPoints([item], true); // 加个true代表顶层，即不绘制 电池堆=>电池簇
    } else if (
      item.children[0].linkElectronStatus === ELECTRON_STATUS_ENUM.discharge
    ) {
      generateLinesAndPoints2([item], true); // 加个true代表顶层，即不绘制 电池堆=>电池簇
    }
  }
  // 电池堆=>电池簇
  for (const item of elData.map((item) => item.children).flat()) {
    if (
      item.linkElectronStatus === ELECTRON_STATUS_ENUM.charge ||
      item.children[0].linkElectronStatus === ELECTRON_STATUS_ENUM.charge // 这个是因为有些电站是 并网柜 => 变压器 => 电池堆 这样的结构，所以需要取多一层children[0]（例如山亚）
    ) {
      generateLinesAndPoints([item]);
    } else if (
      item.linkElectronStatus === ELECTRON_STATUS_ENUM.discharge ||
      item.children[0].linkElectronStatus === ELECTRON_STATUS_ENUM.discharge
    ) {
      generateLinesAndPoints2([item]);
    }
  }
  // 筛选出 electronStatus 为 放电 的对象，放电是从底层到顶层，反向动效
  const filteredData = points.value.filter(
    (item) => item.electronStatus === ELECTRON_STATUS_ENUM.discharge
  );
  const maxIndex = Math.max(...filteredData.map((item) => item.index));
  // 对刚才是筛出来的数据进行反向排序index
  filteredData.forEach((item) => {
    // 计算反向 index
    item.index = maxIndex - item.index + 1;
  });
  // 处理后的数据放回原数组
  points.value.map((item) => {
    if (item.electronStatus === ELECTRON_STATUS_ENUM.discharge) {
      const processedItem = filteredData.find((pItem) => pItem.id === item.id);
      return processedItem || item;
    }
    return item;
  });
};
const handleElements = () => {
  const collectIdLinkStatus = (nodes, idLinkMap) => {
    nodes.forEach((node) => {
      // 如果当前节点有 linkElectronStatus，记录下来
      if (node.systemId !== undefined && node.linkElectronStatus) {
        idLinkMap[node.systemId] = {
          linkElectronStatus: node.linkElectronStatus,
        };
      }

      // 从infoData中获取blockElectronStatus
      const blockElectronStatusItem = node.infoData?.find(
        (item) => item.name === "blockElectronStatus"
      );
      if (node.systemId !== undefined && blockElectronStatusItem) {
        if (!idLinkMap[node.systemId]) {
          idLinkMap[node.systemId] = {};
        }
        idLinkMap[node.systemId].blockElectronStatus =
          blockElectronStatusItem.value;
      }

      // 递归调用处理 children
      if (node.children && Array.isArray(node.children)) {
        collectIdLinkStatus(node.children, idLinkMap);
      }
    });
  };
  // 递归函数，用于更新所有 id 一致的节点
  const updateLinkStatusById = (nodes, idLinkMap) => {
    nodes.forEach((node) => {
      const statusData = idLinkMap[node.systemId];
      if (node.systemId !== undefined && statusData) {
        // 更新 linkElectronStatus
        if (statusData.linkElectronStatus) {
          node.linkElectronStatus = statusData.linkElectronStatus;
        }

        // 更新 blockElectronStatus
        if (statusData.blockElectronStatus) {
          const blockElectronStatusItem = node.infoData?.find(
            (item) => item.name === "blockElectronStatus"
          );
          if (blockElectronStatusItem) {
            blockElectronStatusItem.value = statusData.blockElectronStatus;
          }
          // 同时也设置直接属性作为备用
          node.blockElectronStatus = statusData.blockElectronStatus;
        }
      }
      // 递归调用处理 children
      if (node.children && Array.isArray(node.children)) {
        updateLinkStatusById(node.children, idLinkMap);
      }
    });
  };
  const idLinkMap = {};
  collectIdLinkStatus(elements.value, idLinkMap);
  updateLinkStatusById(elements.value, idLinkMap);
  const addLinkElectronStatusToChildren = (data) => {
    data.forEach((item) => {
      if (item.linkElectronStatus) {
        item.children?.forEach((child) => {
          child.linkElectronStatus = item.linkElectronStatus;
          addLinkElectronStatusToChildren([child]);
        });
      }
      if (
        item.type === EQUIP_TYPE.BIAN_YA_QI ||
        item.type === EQUIP_TYPE.FU_ZAI
      ) {
        item.linkElectronStatus = ELECTRON_STATUS_ENUM.charge;
      }
    });
  };
  // 执行函数以修改 data
  addLinkElectronStatusToChildren(elements.value);
};
const hackBingWangGuiLine = () => {
  // 处理 并网柜 => 上方母线 动效
  let bingWangGuiData = cloneDeep(
    findObjectsByType(elements.value, EQUIP_TYPE.BING_WANG_GUI)
  );
  const fuZaiData = cloneDeep(
    findObjectsByType(elements.value, EQUIP_TYPE.FU_ZAI)
  );
  let lineArr = elements.value.filter((elem) => elem.type === EQUIP_TYPE.LINE);
  const cloneLineArr = lineArr.map((line) => ({ ...line }));
  if (fuZaiData.length) {
    for (const element of bingWangGuiData) {
      // 找出在并网柜上方位置的线
      const upLineArr = cloneLineArr
        .map((line) => ({ ...line }))
        .filter((line) => {
          return (
            element.x >= line.x - 25 && element.x <= line.x + line.width - 25
          );
        });
      if (upLineArr.length) {
        const lineClosestToChild = upLineArr.reduce((closest, current) => {
          const currentDiff = Math.abs(current.y - element.y + 25);
          const closestDiff = Math.abs(closest.y - element.y + 25);
          return currentDiff < closestDiff ? current : closest;
        });
        if (lineClosestToChild) {
          lineClosestToChild.x = cloneDeep(element.x);
          lineClosestToChild.y = lineClosestToChild.y - 25;
          element.children = [lineClosestToChild];
          element.index = 0;
        }
      }
    }
    bingWangGuiData = uniqBy(bingWangGuiData, "id").filter((d) => d);
    bingWangGuiData.forEach((item, index) => {
      // 找到该并网柜下的所有电池堆的的状态
      let belongDianChiDuiStatus = [];
      if (props.stationInfo?.isChargeProject === 1) {
        // 如果是充电桩，linkElectronStatus字段取值和普通项目有所不同
        belongDianChiDuiStatus = elements.value
          .filter(
            (belongDianChiDuiItem) =>
              belongDianChiDuiItem.parentId === item.systemId &&
              belongDianChiDuiItem.type === EQUIP_TYPE.CHONG_DIAN_ZHUANG
          )
          .map((item) => item.linkElectronStatus);
      } else {
        belongDianChiDuiStatus = elements.value
          .filter(
            (belongDianChiDuiItem) =>
              belongDianChiDuiItem.parentId === item.systemId &&
              belongDianChiDuiItem.type === EQUIP_TYPE.DIAN_CHI_DUI
          )
          .map(
            (item) =>
              item.infoData?.find(
                (infoItem) => infoItem.name === "linkElectronStatus"
              ).value
          );
      }
      let linkElectronStatus = ELECTRON_STATUS_ENUM.free;
      if (
        belongDianChiDuiStatus.some(
          (item) => item === ELECTRON_STATUS_ENUM.discharge
        )
      ) {
        linkElectronStatus = ELECTRON_STATUS_ENUM.charge;
      }
      if (
        belongDianChiDuiStatus.some(
          (item) => item === ELECTRON_STATUS_ENUM.charge
        )
      ) {
        linkElectronStatus = ELECTRON_STATUS_ENUM.discharge;
      }
      lines.value.push(
        {
          x1: item.x + 25,
          y1: item.y + 25,
          x2: item.x + 25,
          y2: item.children[0].y + 25,
          isHackLine: true,
        },
        {
          x1: item.x + 25,
          y1: item.y + 25,
          x2: item.x + 25,
          y2: item.children[0].y + 25,
          isBingWangGuiLine: true,
          linkElectronStatus: linkElectronStatus,
        }
      );
    });
  }
};
const initDragData = async () => {
  const { data, userLineName } = props.topologyMap;
  // 用于恢复的逻辑
  if (data) {
    data.forEach((position) => {
      elements.value.push({ ...position });
    });

    if (!elements.value.some((item) => item.id === "topLine")) {
      elements.value.unshift({
        id: "topLine",
        type: EQUIP_TYPE.LINE,
        y: 40,
        children: [],
        lineName: "",
      });
    }
    handleElements();
    // 找到 x和y 最大的值作为整个画布的宽度，后面加上数字是为了留出空间
    const withoutLine = elements.value.filter(
      (item) => item.type !== EQUIP_TYPE.LINE
    );
    leftPosition.value =
      withoutLine.sort((a, b) => b.x - a.x)[0].x +
      (props.stationInfo?.isContainer === 1 ? 600 : 300);
    const spacingHeight = props.stationInfo?.isContainer === 1 ? 300 : 180;
    topPosition.value =
      withoutLine.sort((a, b) => b.y - a.y)[0].y + spacingHeight;
    await nextTick();
    connectLines();
    hackBingWangGuiLine();
    // handleFuZaiAnimate(); // 负载电流动画
    // handleBingWangGuiAnimate(); // 并网柜电流动画
    // handleElectronAnimate(); // 电流动画
    // handlePoints();
    // initPointMove();
  }
  lineName.value = userLineName;
};
const debouncedInitDragData = debounce(initDragData, 100);
watch(
  () => props.topologyMap,
  (val) => {
    if (val) {
      clearInterval(intervalId); // 清理定时器
      lines.value = []; // 重置连接线

      points.value = [];
      originPoints.value = [];

      fuZaiPoints.value = [];
      originFuZaiPoints.value = [];

      bingWangGuiPoints.value = [];
      originBingWangGuiPoints.value = [];

      elements.value = [];
      currentTimeline.value = 0;
      maxIndex.value = 0;
      debouncedInitDragData();
    }
  },
  { deep: true }
);
const originPoints = ref([]);
const maxIndex = ref(0);
const handlePoints = () => {
  points.value = points.value.sort((a, b) => a.parentId - b.parentId);
  let index = 1;
  const parentIdIndexMap = {};
  points.value.forEach((item) => {
    if (!parentIdIndexMap[item.parentId]) {
      parentIdIndexMap[item.parentId] = index++;
    }
    // item.index = parentIdIndexMap[item.parentId];
    Object.assign({}, item);
  });

  const result = [];
  const seen = new Set();
  points.value.forEach((item) => {
    const key = `${item.name1}-${item.name2}`; // 创建一个唯一识别符
    if (!seen.has(key)) {
      seen.add(key); // 记录这个组合
      const { ...rest } = item;
      result.push(rest); // 只推入剩余字段
    }
  });
  points.value = result;
  originPoints.value = cloneDeep(result);
  // maxIndex.value = Math.max(...originPoints.value?.map((item) => item.index));
  const maxIndexValue =
    Array.isArray(originPoints.value) && originPoints.value.length > 0
      ? Math.max(...originPoints.value.map((item) => item.index))
      : 2; // 这里注意：originPoints.value为空，即走进负载判断，maxIndex直接默认给2
  maxIndex.value = maxIndexValue;
};
const getLevel = (arr, targetId, level = 1) => {
  // 循环遍历数组
  for (const item of arr) {
    // 如果找到目标对象，返回当前层级
    if (item.systemId === targetId) {
      return level;
    }
    // 如果当前对象有子级，递归查找子级
    if (item.children && item.children.length > 0) {
      const childLevel = getLevel(item.children, targetId, level + 1);
      if (childLevel !== -1) {
        return childLevel; // 如果子级中找到了目标对象，返回层级
      }
    }
  }
  return -1; // 未找到目标对象
};
const fuZaiPoints = ref([]);
const fuZaiGenerateLinesAndPoints = (nodes, index) => {
  // 先做一个判断，如果变压器关口表上的功率 <=0 ，则不生成动态光点，直接置空。注意：判断时由于变压器数据挂载到关口表上，所以匹配时需要拿拓扑图数据的parentId去匹配当前的nodes.systemId
  const bianYaQI = cloneDeep(nodes);
  const gateWayP = props.topologyMap.data
    ?.find((item) => item.parentId === bianYaQI.systemId)
    ?.infoData?.find((item) => item.name === "P")?.value;
  if (gateWayP === "*" || gateWayP <= 0) {
    fuZaiPoints.value[index] = [];
    return;
  }
  if (bianYaQI.type === EQUIP_TYPE.BIAN_YA_QI && bianYaQI.children.length) {
    const line = bianYaQI.children[0];
    const point = {
      startX: bianYaQI.x + 25,
      startY: bianYaQI.y + 25,
      endX: bianYaQI.x + 25,
      endY: line.y + 25,
      index: 1,
      name1: bianYaQI.name,
      name2: line.lineName,
    };
    fuZaiPoints.value[index][0] = point; // 添加光点
  }
  if (
    bianYaQI.children[0]?.type === EQUIP_TYPE.LINE &&
    bianYaQI.children[0]?.children[0]?.type === EQUIP_TYPE.FU_ZAI
  ) {
    const line = bianYaQI.children[0];
    const fuZai = bianYaQI.children[0].children[0];
    const point1 = {
      startX: line.x + 25,
      startY: line.y + 25,
      endX: fuZai.x + 25,
      endY: line.y + 25,
      index: 2,
      name1: line.lineName,
      name2: line.lineName,
    };
    const point2 = {
      startX: fuZai.x + 25,
      startY: line.y + 25,
      endX: fuZai.x + 25,
      endY: fuZai.y + 25,
      index: 3,
      name1: line.lineName,
      name2: fuZai.name,
    };
    fuZaiPoints.value[index][1] = point1; // 添加光点
    fuZaiPoints.value[index][2] = point2; // 添加光点
  } else {
    fuZaiPoints.value = [];
  }
};
const bingWangGuiPoints = ref([]);
const bingWangGuiGenerateLinesAndPoints = (nodes, index) => {
  // 先做一个判断，必须是 放电 状态，才需要生成并网柜 => 负载的动画
  // 找到该并网柜下的所有电池堆的的状态
  const bingWangGui = cloneDeep(nodes);
  const belongDianChiDuiStatus = elements.value
    .filter(
      (item) =>
        item.parentId === bingWangGui.systemId &&
        item.type === EQUIP_TYPE.DIAN_CHI_DUI
    )
    .map(
      (item) =>
        item.infoData?.find(
          (infoItem) => infoItem.name === "linkElectronStatus"
        ).value
    );
  // 如果不是全是放电状态，不出现动画
  if (
    belongDianChiDuiStatus.some(
      (item) => item !== ELECTRON_STATUS_ENUM.discharge
    )
  ) {
    bingWangGuiPoints.value[index] = [];
    return;
  }
  if (
    bingWangGui.type === EQUIP_TYPE.BING_WANG_GUI &&
    bingWangGui.children.length
  ) {
    const line = bingWangGui.children[0];
    const point = {
      startX: bingWangGui.x + 25,
      startY: bingWangGui.y + 25,
      endX: bingWangGui.x + 25,
      endY: line.y + 25,
      index: 1,
      name1: bingWangGui.name,
      name2: line.lineName,
    };
    bingWangGuiPoints.value[index][0] = point; // 添加光点
  }
  if (
    bingWangGui.children[0]?.type === EQUIP_TYPE.LINE &&
    bingWangGui.children[0]?.children[0]?.type === EQUIP_TYPE.FU_ZAI
  ) {
    const line = bingWangGui.children[0];
    const fuZai = bingWangGui.children[0].children[0];
    const point1 = {
      startX: bingWangGui.x + 25,
      startY: line.y + 25,
      endX: fuZai.x + 25,
      endY: line.y + 25,
      index: 2,
      name1: line.lineName,
      name2: line.lineName,
    };
    const point2 = {
      startX: fuZai.x + 25,
      startY: line.y + 25,
      endX: fuZai.x + 25,
      endY: fuZai.y + 25,
      index: 3,
      name1: line.lineName,
      name2: fuZai.name,
    };
    bingWangGuiPoints.value[index][1] = point1; // 添加光点
    bingWangGuiPoints.value[index][2] = point2; // 添加光点
  } else {
    bingWangGuiPoints.value = [];
  }
};
const generateLinesAndPoints = (nodes, isTop = false) => {
  nodes.forEach((node) => {
    // 如果有子节点
    if (node.children) {
      // 为每个子节点生成光点
      node.children.forEach((child) => {
        const point = {
          startX: node.x + 25,
          startY: node.y + 25,
          endX: child.x + 25,
          endY: child.y + 25,
          index: child.index || 1,
          name1: node.name || node.lineName,
          name2: child.name || child.lineName,
          parentId: node.parentId,
          type: child.type,
          electronStatus: ELECTRON_STATUS_ENUM.charge, // 充电
        };
        points.value.push(point); // 添加光点

        // 递归调用生成子节点的线段和光点
        if (!isTop) {
          generateLinesAndPoints(node.children, node.x, node.y);
        }
      });
    }
  });
};
const generateLinesAndPoints2 = async (nodes, isTop = false) => {
  const processNode = (node) => {
    if (isTop && node.type === 4) {
      return;
    }
    if (node.children) {
      // 先处理所有子节点
      node.children.forEach((child) => processNode(child));
      // 处理当前节点与子节点之间的连接
      node.children.forEach((child, index) => {
        const point = {
          startX: child.x + 25,
          startY: child.y + 25,
          endX: node.x + 25,
          endY: node.y + 25,
          index: child.index,
          name1: child.name,
          name2: node.name,
          parentId: node.parentId,
          electronStatus: ELECTRON_STATUS_ENUM.discharge, // 放电
        };
        points.value.push(point); // 添加光点
      });
    }
  };
  // 从根节点开始后序遍历
  nodes.forEach((node) => processNode(node));
};

const currentTimeline = ref(0);
let intervalId = null;
const initPointMove = () => {
  intervalId = setInterval(() => {
    // 清空点数组
    points.value = [];
    fuZaiPoints.value = [];
    bingWangGuiPoints.value = [];
    // 两秒后重置点，给100ms的空白时间
    setTimeout(() => {
      points.value = [...originPoints.value];
      fuZaiPoints.value = [...originFuZaiPoints.value];
      bingWangGuiPoints.value = [...originBingWangGuiPoints.value];
    }, 100);
    // 更新 currentTimeline
    const prefixMaxIndex = maxIndex.value || 2;
    currentTimeline.value += prefixMaxIndex * 2;
  }, maxIndex.value * 2 * 1000);
};
const elementData = ref({});
const pcsData = ref({});
const prefixStatus = {
  1: "空闲",
  2: "充电",
  3: "放电",
};

// 这里的 type 是给储充项目，区分是哪个block
const handleEquip = async (element, type) => {
  elementData.value = {
    ...element,
    blockType: type,
  };
  if (
    element.type === EQUIP_TYPE.DIAN_CHI_CU ||
    element.type === EQUIP_TYPE.CHONG_DIAN_ZHUANG ||
    element.type === EQUIP_TYPE.BATTERY_CLUSTER
  ) {
    getBatteryPackInfo();
    powerBlockAssetCode(element);
    const params = {
      stationId: props.stationInfo.id,
      componentId: element.systemId,
    };
    const res = await $api.stationRealtimeChild(params);
    if (res && res.code === 0) {
      showMap.value = false;
      changeShowBackBtn(true);
      pcsData.value = res.data[element.systemId];
      if (pcsData.value) {
        for (const item of pcsData.value) {
          if (item.name === "工作状态" && item.dataType === 1) {
            item.value = prefixStatus[item.value];
          }
        }
      }
    }
  }
};
const handleBack = () => {
  showMap.value = true;
  changeShowBackBtn(false);
};
const batteryInfo = ref({});
const getBatteryPackInfo = async () => {
  const params = {
    stationId: props.stationInfo.id,
  };
  const res = await $api.batteryPackInfo(params);
  if (res && res.code === 0) {
    batteryInfo.value = res.data;
  }
};
const assetCodeData = ref({});
const powerBlockAssetCode = async (element) => {
  const res = await $api.powerBlockAssetCode(element.systemId);
  if (res && res.code === 0) {
    assetCodeData.value = res.data;
  } else {
    assetCodeData.value = {};
  }
};
const getSocVal = (element) => {
  const soc = element?.infoData?.find((item) => item.name === "SOC")?.value;
  if (soc) {
    // 转成百分比
    return soc / 100;
  } else {
    return 0;
  }
};
const handleFlowAnimation = (line) => {
  // 集装箱项目优先特殊处理
  if (props.stationInfo?.isContainer) {
    if (line.linkElectronStatus === ELECTRON_STATUS_ENUM.discharge) {
      return "dischargeMove";
    } else if (line.linkElectronStatus === ELECTRON_STATUS_ENUM.charge) {
      return "chargeMove";
    } else {
      // 电池簇除了充放电，其他不动
      return "";
    }
  }
  if (
    line.isChildEquip ||
    line.linkElectronStatus === "" ||
    line.linkElectronStatus === undefined ||
    line.linkElectronStatus === ELECTRON_STATUS_ENUM.free
  ) {
    return "";
  }
  if (
    line.parentType === EQUIP_TYPE.BIAN_YA_QI ||
    line.childType === EQUIP_TYPE.BIAN_YA_QI
  ) {
    // 如果是变压器区域线，必须 P > 0 才有动效
    if (line.gateWayP > 0) {
      return "chargeMove";
    } else {
      return "";
    }
  } else if (
    line.parentType === EQUIP_TYPE.DIAN_CHI_CU ||
    line.childType === EQUIP_TYPE.DIAN_CHI_CU
  ) {
    if (line.blockElectronStatus === ELECTRON_STATUS_ENUM.discharge) {
      return "dischargeMove";
    } else if (line.blockElectronStatus === ELECTRON_STATUS_ENUM.charge) {
      return "chargeMove";
    } else {
      // 电池簇除了充放电，其他不动
      return "";
    }
  } else {
    if (line.linkElectronStatus === ELECTRON_STATUS_ENUM.discharge) {
      return "dischargeMove";
    } else {
      return "chargeMove";
    }
  }
};

const handleFlowLineType = (line) => {
  if (
    line.parentType === EQUIP_TYPE.BIAN_YA_QI ||
    line.childType === EQUIP_TYPE.BIAN_YA_QI
  ) {
    // 如果是变压器区域线，必须 P > 0 才有是虚线，否则实线
    if (line.gateWayP > 0) {
      return "5, 5";
    } else {
      return "1000, 0";
    }
  } else if (
    line.parentType === EQUIP_TYPE.DIAN_CHI_CU ||
    line.childType === EQUIP_TYPE.DIAN_CHI_CU
  ) {
    if (
      line.blockElectronStatus === ELECTRON_STATUS_ENUM.discharge ||
      line.blockElectronStatus === ELECTRON_STATUS_ENUM.charge
    ) {
      // 电池簇除了充放电，其他都是实线
      return "5, 5";
    } else {
      return "1000, 0";
    }
  } else if (
    line.parentType === EQUIP_TYPE.JI_ZHUANG_XIANG ||
    line.childType === EQUIP_TYPE.BATTERY_CLUSTER
  ) {
    if (
      line.linkElectronStatus === ELECTRON_STATUS_ENUM.discharge ||
      line.linkElectronStatus === ELECTRON_STATUS_ENUM.charge
    ) {
      // 电池簇除了充放电，其他都是实线
      return "5, 5";
    } else {
      return "1000, 0";
    }
  } else {
    // 非变压器区域，通过充放电状态判断虚实线
    if (
      line.linkElectronStatus === ELECTRON_STATUS_ENUM.discharge ||
      line.linkElectronStatus === ELECTRON_STATUS_ENUM.charge ||
      line.isChildEquip
    ) {
      return "5, 5";
    } else {
      return "1000, 0";
    }
  }
};
onMounted(() => {});
const handleChageStation = () => {
  handleBack();
};
defineExpose({ handleBack, handleChageStation });

// 处理窗口调整大小
window.addEventListener("resize", connectLines);
</script>
<style lang="less" scoped>
.canvas {
  position: relative;
  width: 2000px;
  height: 2000px;
}
.topContent {
  height: 120px;
  line-height: 79px;
  position: absolute;
  left: 0;
  width: 100%;
}
.topIcon {
  background: url(/src/assets/drag/dianWang.png) no-repeat center center;
  width: 50px;
  height: 50px;
  left: 48px;
  top: 5px;
  position: absolute;
}
.topLine {
  top: 90px;
  left: 120px;
  width: 100%;
  height: 4px;
  background: red;
  position: absolute;
}
.topLineName {
  left: 118px;
  top: 35px;
  display: inline-block;
  position: absolute;
  width: 500px;
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
  text-align: left;
}
.canvas-element {
  position: absolute;
  background-color: #202020;
}
.canvasElementName {
  position: absolute;
  right: -120px;
  bottom: 0;
  width: 120px;
  pointer-events: none;
}
.percent {
  width: 12px;
  height: 20px;
  position: absolute;
  left: 50%;
  bottom: 11px;
  transform: translate(-50%, 0);
  background: #5fd767;
}
.cursor {
  cursor: pointer;
}
.detailContent {
  position: absolute;
  width: 100px;
  left: 51px;
  font-size: 12px;
  .detailItem {
    margin-top: 2px;
    cursor: pointer;
  }
  .P {
    color: #d9001b;
  }
  .Q,
  .V {
    color: #00bfbf;
  }
  .Ia,
  .A {
    color: #ffff00;
  }
  .Ua,
  .SOC {
    color: #f59a23;
  }
  .PF,
  .SOH {
    color: #c280ff;
  }
}
.jiZhuangXiangDetailContent {
  top: -72px;
  left: 35px;
}
.linkDetailContent {
  position: absolute;
  white-space: nowrap;
  left: 2px;
  font-size: 12px;
  top: 29px;
  background: #555555;
  padding: 5px;
  border-radius: 4px;
  line-height: 1.5;
  display: none;
  z-index: 10;
}
.warnning {
  cursor: pointer;
  &:hover {
    .linkDetailContent {
      display: block;
    }
  }
}
.warnMessage {
  color: #f59a23;
  text-align: left;
}
.linkStatus {
  position: absolute;
  left: 40px;
  top: 0;
  // width: 120px;
  .offline {
    color: #000;
    margin-bottom: 10px;
    &::before {
      content: "";
      display: inline-block;
      width: 5px;
      height: 5px;
      border-radius: 50%;
      margin-right: 5px;
      background: #000;
      position: relative;
      top: -1px;
    }
  }
  .warnning {
    color: #f59a23;
    &::before {
      content: "";
      display: inline-block;
      width: 5px;
      height: 5px;
      border-radius: 50%;
      margin-right: 5px;
      background: #f59a23;
      position: relative;
      top: -1px;
    }
  }
  .offline,
  .warnning {
    width: 50px;
    margin-right: 5px;
    font-size: 12px;
    background: #fff;
    text-align: center;
    height: 20px;
    line-height: 20px;
    border-radius: 12px;
    display: inline-block;
  }
}
.jzxLinkStatus {
  left: 238px;
  top: 0;
}
.isChildEquip {
  background-size: 70% !important;
  background-position: center center !important;
}
// .customLineBar {
//   width: 100%;
// }
.customLine {
  width: 100%;
  height: 4px;
  background: red;
  display: block;
}
.line {
  position: relative;
  .lineName {
    position: absolute;
    bottom: 10px;
  }
}
.elementEquip {
  position: relative;
  &:hover {
    .assertCode {
      display: block;
    }
  }
}
.assertCode {
  position: absolute;
  left: 100%;
  top: 50%;
  transform: translateY(-50%);
  margin-left: 10px;
  background-color: rgba(50, 50, 50, 0.9);
  color: #fff;
  padding: 8px 12px;
  border-radius: 6px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
  font-size: 13px;
  line-height: 1.5;
  z-index: 100;
  width: max-content;
  white-space: nowrap;
  text-align: left;
  pointer-events: none;
  display: none;
}

.assertCode div {
  margin-bottom: 6px;
}

.assertCode div:last-child {
  margin-bottom: 0;
}
.chargeMove {
  stroke-dashoffset: 20;
  animation: move 1s linear infinite;
}
.dischargeMove {
  stroke-dashoffset: -20;
  animation: move 1s linear infinite;
}
.bingWangGuiMove {
  stroke-dashoffset: 20;
  animation: move 1s linear infinite;
}
@keyframes move {
  to {
    stroke-dashoffset: 0;
  }
}
.displayNone {
  display: none;
}
.chongDianZhuang {
  width: 300px;
  height: 150px;
  background-position: center center;
}
.ant-divider-horizontal {
  margin: 0;
}
</style>
